package task

import (
	"fmt"

	"file_explorer/common/log"
	"file_explorer/common/util"
	"file_explorer/model"
)

type BatchCopier struct {
	BasicTask

	srcFileInfos []*model.FileInfo
	dstDir       string

	copyItems []*copyItem
	totalSize int64

	walker *PathWalker
	copier *fileCopier
}

type copyItem struct {
	info    *model.FileInfo
	dstPath string
}

func (task *BatchCopier) GetLabel() string {
	name := task.srcFileInfos[0].Name
	if len(task.srcFileInfos) > 1 {
		name += fmt.Sprintf("等%d项", len(task.srcFileInfos))
	}
	return fmt.Sprintf("复制%v到%v", name, task.dstDir)
}

func (task *BatchCopier) Pause() {
	if task.walker != nil {
		task.walker.Pause()
	}
	if task.copier != nil {
		task.copier.Pause()
	}
}

func (task *BatchCopier) Cancel() {
	task.BasicTask.Cancel()
	if task.walker != nil {
		task.walker.Cancel()
	}
	if task.copier != nil {
		task.copier.Cancel()
	}
}

func (task *BatchCopier) start() {
	task.BasicTask.start()
	defer task.BasicTask.finish()

	task.preProcess()
	task.scan()
	task.batchCopy()
}

func (task *BatchCopier) preProcess() {
	log.Debug("batch copier pre process start")

	if len(task.srcFileInfos) == 0 {
		task.fail(fmt.Errorf("未选中任何文件"))
		return
	}

	for _, srcFileInfo := range task.srcFileInfos {
		if srcFileInfo.IsDir() && (srcFileInfo.GetPath() == task.dstDir || srcFileInfo.IsParent(task.dstDir)) {
			task.fail(fmt.Errorf("父目录不可复制到子目录下"))
			return
		}
	}

	log.Debug("batch copier pre process end")
}

func (task *BatchCopier) scan() {
	log.Debug("batch copier scan src files start")

	task.walker = NewPathWalker(task.srcFileInfos)
	rootRenameMap := make(map[string]string)
	task.walker.SetOnAccess(func(root string, info *model.FileInfo) {
		task.progress = fmt.Sprintf("扫描中: %v", info.GetPath())

		srcPath := info.GetPath()
		relativePath := srcPath[len(root):]
		rootDir, rootName := util.PathSplit(root)

		// 同目录拷贝时，创建副本
		if tmp, ok := rootRenameMap[root]; ok {
			rootName = tmp
		} else if rootDir == task.dstDir {
			newRoot, err := util.ResolveFilenameConflict(root)
			if err != nil {
				task.walker.fail(err)
				return
			}
			_, rootName = util.PathSplit(newRoot)
		}

		rootRenameMap[root] = rootName

		task.copyItems = append(task.copyItems, &copyItem{
			info:    info,
			dstPath: util.PathJoin(task.dstDir, rootName, relativePath),
		})

		if !info.IsDir() {
			task.totalSize += info.Size
		}
	})
	task.walker.SetOnFinish(func(_ int64, _ bool, err error) {
		task.fail(err)
	})
	task.walker.start()

	log.Debug("batch copier scan src files end")
}

func (task *BatchCopier) batchCopy() {
	log.Debug("batch copier copy start")

	copiedFileSize := int64(0)
	for _, item := range task.copyItems {
		if !task.isRunning() {
			break
		}

		if item.info.IsDir() {
			err := util.MkdirIfNotExist(item.dstPath)
			if err != nil {
				task.fail(err)
				return
			}
			continue
		}

		task.copier = newFileCopier(item.info, item.dstPath, func(curFileCopiedSize int64) {
			copiedSize := copiedFileSize + curFileCopiedSize
			task.percentage = float64(copiedSize) / float64(task.totalSize)
			task.progress = fmt.Sprintf("%.2f%%, %s/%s, 复制%v中",
				task.percentage*100, util.ConvSize(copiedSize), util.ConvSize(task.totalSize), item.info.Name)
		})
		task.copier.SetOnFinish(func(_ int64, _ bool, err error) {
			if !item.info.IsDir() {
				copiedFileSize += item.info.Size
			}
			task.fail(err)
		})
		task.copier.start()
	}

	log.Debug("batch copier copy end")
}

func NewBatchCopier(srcFileInfos []*model.FileInfo, dstDir string) *BatchCopier {
	return &BatchCopier{
		BasicTask:    newBasicTask("batch copier"),
		srcFileInfos: srcFileInfos,
		dstDir:       dstDir,
	}
}
